﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Scrumrun.Models;
using System.Globalization;
using System.Web.Security;

namespace Scrumrun.Controllers
{
    [Authorize]
    public class ProjectController : Controller
    {
        private ProjectDbContext db = new ProjectDbContext();

        //
        // GET: /Project/
        
        public ActionResult Index()
        {

            //Guid userGuid = (Guid)Membership.GetUser().ProviderUserKey;

            return View(db.Projects.ToList());
        }

        //
        // GET: /Project/Details/5
        public ActionResult Details(int id = 0)
        {
            Project project = db.Projects.Find(id);

            if (project == null)
            {
                return HttpNotFound();
            }


            Sprint sprint = db.Sprints
                            .Where(s => s.Project_ProjectId == id && s.StartDate <= DateTime.Today && s.EndDate >= DateTime.Today)
                            .FirstOrDefault() ?? null;

            if (sprint != null)
            {
                ViewBag.totalDays = numberOfDays(project.StartDate, project.EndDate);
                ViewBag.remainingDays = numberOfDays(project.StartDate, project.EndDate) - numberOfDays(project.StartDate, DateTime.Today);

                ViewBag.currentSprint = sprint.Name;
                ViewBag.sprintStart = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(sprint.StartDate.Month) + " " + sprint.StartDate.Day;
                ViewBag.sprintEnd = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(sprint.EndDate.Month) + " " + sprint.EndDate.Day;
            }

            return View(project);
        }

        //
        // GET: /Project/Create
        [Authorize(Roles="Admin")]
        public ActionResult Create()
        {
            Project newPrj = new Project();

            newPrj.NumOfIterationDay = 30;
            newPrj.StartDate = DateTime.Today;
            newPrj.EndDate = businessDaysFromStartDate(DateTime.Today,30);
            newPrj.WorkingDaysOnly = true;

            return View(newPrj);
        }

        //
        // POST: /Project/Create

        [HttpPost]
        [Authorize(Roles = "Admin")]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Project project)
        {
            if (ModelState.IsValid)
            {
                //TODO end date must be greater than start date

                //TODO get sprint list

                List<Sprint> sprints = getSprints(project.StartDate,
                    project.EndDate, project.NumOfIterationDay, project.WorkingDaysOnly);

                for (int i = 0; i < sprints.Count; i++)
                {
                    sprints[i].Name = "Sprint " + (i + 1);
                    sprints[i].Project_ProjectId = project.ProjectId;
                    sprints[i].Project = project;

                    project.Sprints.Add(sprints[i]);
                    db.Sprints.Add(sprints[i]);
                }

                db.Projects.Add(project);

                //db.Sprints.Add(s1);

                db.SaveChanges();
                return RedirectToAction("Index");
            }

            //return RedirectToAction("Index");
            return View(project);
        }

        //
        // GET: /Project/Edit/5

        public ActionResult Edit(int id = 0)
        {
            Project project = db.Projects.Find(id);
            if (project == null)
            {
                return HttpNotFound();
            }

            
            return View(project);
        }

        //
        // POST: /Project/Edit/5

        [HttpPost]
        public ActionResult Edit(Project project)
        {
            if (ModelState.IsValid)
            {
                db.Entry(project).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return RedirectToAction("Index");

            //return View(project);
        }

        //
        // GET: /Project/Delete/5

        public ActionResult Delete(int id = 0)
        {
            Project project = db.Projects.Find(id);
            if (project == null)
            {
                return HttpNotFound();
            }
            return View(project);
        }

        //
        // POST: /Project/Delete/5

        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(int id)
        {
            Project project = db.Projects.Find(id);


            project.Sprints.ToList().ForEach(s => db.Sprints.Remove(s));

            //ICollection<Sprint> sprints = project.Sprints;
            //foreach(Sprint s in sprints){
            //    db.Sprints.Remove(s);
            //}

            project.UserStories.ToList().ForEach(us => db.UserStories.Remove(us));

            //ICollection<UserStory> userstories = project.UserStories;
            //foreach (UserStory us in userstories)
            //{
            //    db.UserStories.Remove(us);
            //}

            db.Projects.Remove(project);

            db.SaveChanges();
            return RedirectToAction("Index");
        }




        public ActionResult EditSprints(int ProjectId = 0)
        {
            if (ProjectId > 0)
                HttpContext.Session["CurrentProject"] = ProjectId.ToString();
            else if (HttpContext.Session["CurrentProject"] == null)
                Redirect("/Project/Index");
            else
                ProjectId = Int32.Parse(HttpContext.Session["CurrentProject"].ToString());

            ViewBag.CurrentProject = ProjectId.ToString();

            Project project = db.Projects.Find(ProjectId);
            if (project == null)
            {
                return HttpNotFound();
            }


            return View(project);
        }


        [HttpPost]
        public ActionResult EditSprints(Project project)
        {
            //Project project = db.Projects.Find(ProjectId);
            Project prj = db.Projects.Find(project.ProjectId);

            if (prj != null)
            {
                prj.NumOfIterationDay = project.NumOfIterationDay;
                prj.WorkingDaysOnly = project.WorkingDaysOnly;

                               
           //Clear previous sprints
                //clear from userstories
                List<UserStory> userstories = db.UserStories
                    .Where(us => us.Project_ProjectId == prj.ProjectId && us.Sprint.StartDate > DateTime.Today)
                    .ToList();

                foreach (UserStory us in userstories)
                {
                    us.Sprint = null;
                    us.Sprint_SprintId = null;
                    db.Entry(us).State = EntityState.Modified;
                }

                //Clear from project
                List<Sprint> sprintToremove = prj.Sprints.Where(s => s.StartDate > DateTime.Today).ToList();
                foreach (Sprint sp in sprintToremove)
	             {
                    sp.Project = null;
                    sp.Project_ProjectId = 0;
                    sp.UserStories.Clear();
                    prj.Sprints.Remove(sp);
                    db.Sprints.Remove(sp);
	             }

                DateTime startdate;
                Sprint cur = prj.Sprints.Where(sp => sp.EndDate > DateTime.Today).FirstOrDefault() ?? null;
                if (cur != null)
                {
                    startdate = cur.EndDate.AddDays(1);
                }
                else
                {
                    startdate = prj.StartDate;
                }

           //Add new sprints

                List<Sprint> sprints = getSprints(startdate,
                        prj.EndDate, prj.NumOfIterationDay, prj.WorkingDaysOnly);

                int nameCount = prj.Sprints.Count + 1;
                for (int i = 0; i < sprints.Count; i++)
                {
                    sprints[i].Name = "Sprint " + nameCount++;
                    sprints[i].Project_ProjectId = prj.ProjectId;
                    sprints[i].Project = prj;

                    prj.Sprints.Add(sprints[i]);
                    db.Sprints.Add(sprints[i]);
                }

                //foreach (UserStory us in userstories)
                //{
                    
                //    us.Sprint = sprints[0];
                //    us.Sprint_SprintId = sprints[0].SprintId;
                //    sprints[0].UserStories.Add(us);

                //    db.Entry(us).State = EntityState.Modified;
                //}

                db.Entry(prj).State = EntityState.Modified;

                db.SaveChanges();

            }



            List<Sprint> nsprints = db.Sprints
                .Where(s => s.Project_ProjectId == prj.ProjectId)
                .OrderBy(s => s.SprintId).ToList();

            return PartialView("_SprintTable", nsprints);

        }


        [HttpPost]
        public ActionResult GetGraph(int ProjectId)
        {
            //{"name":"John"}
            GraphData graphData = new GraphData();

            Project prj = db.Projects.Find(ProjectId);
            int maxx = numberOfDays(prj.StartDate, prj.EndDate);
            int maxy = prj.UserStories.Select(s => s.Effort).Sum();
            int[][] tmpd2 = new int[maxx + 1][];
            int my = 0;

            tmpd2[0] = new int[2] {0 , maxy};

            for (int i = 1; i <= maxx; i++)
            {
                if (prj.StartDate.AddDays(i) > DateTime.Today) break;

                my = maxy - prj.UserStories.Where(us => us.CompletedDate <= prj.StartDate.AddDays(i))
                        .Select(s => s.Effort).Sum();

                tmpd2[i] = new int[2] { i, my };
            }

            //int[][] tmpd2 = new int[][] { new int[] { 0, 20 }, 
            //                                new int[] { 3, 19 }, 
            //                                new int[] { 5, 18 }, 
            //                                new int[] { 7, 10 } };

            graphData.maxx = maxx;//total days
            graphData.maxy = maxy;//total effort

            graphData.d2 = tmpd2;//total effort of work completed each day. [day,effort]
            return Json(graphData, JsonRequestBehavior.AllowGet);
        }


        public ActionResult TodaysTasks()
        {
            List<UserStory> userstories = db.UserStories
                .Where(us => us.UserProfile_UserId == WebMatrix.WebData.WebSecurity.CurrentUserId
                && us.CompletedDate == null).ToList();

            return PartialView(userstories);
        }

        [ChildActionOnly]
        [AllowAnonymous]
        public ActionResult MainMenu()
        {
            List<MenuItemModel> menu = new List<MenuItemModel>();

            if (WebMatrix.WebData.WebSecurity.IsAuthenticated)
            {
                menu.Add(new MenuItemModel { Text = "Home", Action = "Index", Controller = "Project", Selected = false });

                string projectid = "";
                try
                {
                    projectid = ControllerContext.HttpContext.Request.QueryString["ProjectId"].ToString();
                }
                catch (Exception ex)
                {
                    try
                    {
                        projectid = ControllerContext.ParentActionViewContext.RouteData.Values["id"].ToString();
                    }
                    catch (Exception ex2)
                    { }
                }

                if (projectid.Length > 0)
                {
                    menu.Add(new MenuItemModel { Text = "Project", Action = "Details", Controller = "Project", Selected = false, Routedata = new { id = projectid } });
                    menu.Add(new MenuItemModel { Text = "Backlog", Action = "index", Controller = "Backlog", Selected = false, Routedata = new { ProjectId = projectid } });
                    menu.Add(new MenuItemModel { Text = "Scrumboard", Action = "sprintboard", Controller = "Sprint", Selected = false, Routedata = new { ProjectId = projectid } });
                }

            }

            return PartialView(menu);
        }

        public System.DateTime businessDaysFromStartDate (System.DateTime StartDate, int NumberOfBusinessDays)
        {
            //Knock the start date down one day if it is on a weekend.
            if (StartDate.DayOfWeek == DayOfWeek.Saturday |
                StartDate.DayOfWeek == DayOfWeek.Sunday)
            {
                NumberOfBusinessDays -= 1;
            }

            int index = 0;

            for (index = 1; index <= NumberOfBusinessDays; index++)
            {
                switch (StartDate.DayOfWeek)
                {
                    case DayOfWeek.Sunday:
                        StartDate = StartDate.AddDays(2);
                        break;
                    case DayOfWeek.Monday:
                    case DayOfWeek.Tuesday:
                    case DayOfWeek.Wednesday:
                    case DayOfWeek.Thursday:
                    case DayOfWeek.Friday:
                        StartDate = StartDate.AddDays(1);
                        break;
                    case DayOfWeek.Saturday:
                        StartDate = StartDate.AddDays(3);
                        break;
                }
            }

            //check to see if the end date is on a weekend.
            //If so move it ahead to Monday.
            //You could also bump it back to the Friday before if you desired to. 
            //Just change the code to -2 and -1.

            if (StartDate.DayOfWeek == DayOfWeek.Saturday)
            {
                StartDate = StartDate.AddDays(2);
            }
            else if (StartDate.DayOfWeek == DayOfWeek.Sunday)
            {
                StartDate = StartDate.AddDays(1);
            }

            return StartDate;
        }

        public int numberOfDays(DateTime Start, DateTime End)
        {
            TimeSpan ts;
            if(End > Start)
                ts = End - Start;
            else
                ts = Start - End;

            return ts.Days;
        }

        public System.DateTime addBusinessDays(System.DateTime StartDate, int NumberOfBusinessDays)
        {
            //Knock the start date down one day if it is on a weekend.
            if (StartDate.DayOfWeek == DayOfWeek.Saturday |
                StartDate.DayOfWeek == DayOfWeek.Sunday)
            {
                NumberOfBusinessDays -= 1;
            }

            int index = 0;

            for (index = 1; index <= NumberOfBusinessDays; index++)
            {
                switch (StartDate.DayOfWeek)
                {
                    case DayOfWeek.Sunday:
                        StartDate = StartDate.AddDays(2);
                        break;
                    case DayOfWeek.Monday:
                    case DayOfWeek.Tuesday:
                    case DayOfWeek.Wednesday:
                    case DayOfWeek.Thursday:
                    case DayOfWeek.Friday:
                        StartDate = StartDate.AddDays(1);
                        break;
                    case DayOfWeek.Saturday:
                        StartDate = StartDate.AddDays(3);
                        break;
                }
            }

            //check to see if the end date is on a weekend.
            //If so move it ahead to Monday.
            //You could also bump it back to the Friday before if you desired to. 
            //Just change the code to -2 and -1.

            if (StartDate.DayOfWeek == DayOfWeek.Saturday)
            {
                StartDate = StartDate.AddDays(2);
            }
            else if (StartDate.DayOfWeek == DayOfWeek.Sunday)
            {
                StartDate = StartDate.AddDays(1);
            }

            return StartDate;
        }

        public List<Sprint> getSprints(DateTime startDt, DateTime endDt, int interval, bool workingDaysOnly)
        {
            List<Sprint> sprints = new List<Sprint>();

            interval--;
            while (true)
            {
                Sprint s = new Sprint();
                s.StartDate = startDt;

                if (workingDaysOnly && addBusinessDays(startDt, interval) > endDt)
                {
                    s.EndDate = endDt;
                    sprints.Add(s);
                    break;
                }
                else if (!workingDaysOnly && startDt.AddDays(interval) > endDt)
                {
                    s.EndDate = endDt;
                    sprints.Add(s);
                    break;
                }
                else
                {
                    s.EndDate = workingDaysOnly ? addBusinessDays(startDt, interval) : startDt.AddDays(interval);
                }

                startDt = workingDaysOnly ? addBusinessDays(startDt, interval + 1) : startDt.AddDays(interval + 1);
                if(startDt <= endDt)
                    sprints.Add(s);
            }

            return sprints;
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }


    }

    class GraphData
    {
        public int maxx { get; set; }
        public int maxy { get; set; }
        public int[][] d2 { get; set; }
    }
}